<?php
/**
 * 测试基类
 *
 * @author Claude Code
 * @since 2025-11-03
 */

namespace tests;

use PHPUnit\Framework\TestCase as PHPUnitTestCase;
use think\facade\Db;
use think\facade\Cache;
use think\facade\Config;

/**
 * 单元测试基类
 *
 * 提供：
 * 1. 数据库操作模拟
 * 2. 缓存管理
 * 3. 测试数据准备
 * 4. 测试环境清理
 */
abstract class TestCase extends PHPUnitTestCase
{
    /**
     * 测试数据库连接
     * @var \think\db\Connection
     */
    protected $db;

    /**
     * 测试配置
     */
    protected function setUp(): void
    {
        parent::setUp();

        // 初始化数据库连接（使用内存数据库进行测试）
        $this->initDatabase();

        // 清除缓存
        $this->clearCache();

        // 准备测试数据
        $this->prepareTestData();
    }

    /**
     * 清理测试环境
     */
    protected function tearDown(): void
    {
        // 清理测试数据
        $this->cleanupTestData();

        // 关闭数据库连接
        $this->closeDatabase();

        // 清除缓存
        $this->clearCache();

        parent::tearDown();
    }

    /**
     * 初始化测试数据库
     */
    protected function initDatabase()
    {
        // 配置测试数据库（SQLite内存数据库）
        Db::setConfig([
            'default' => 'test',
            'connections' => [
                'test' => [
                    'type' => 'sqlite',
                    'database' => ':memory:',
                    'prefix' => '',
                    'charset' => 'utf8',
                ]
            ]
        ]);

        // 重新连接数据库
        $this->db = Db::connect();

        // 创建测试表结构
        $this->createTestTables();
    }

    /**
     * 创建测试表
     */
    protected function createTestTables()
    {
        // 部门表
        $this->db->execute('
            CREATE TABLE IF NOT EXISTS oa_department (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title VARCHAR(50) NOT NULL,
                pid INTEGER DEFAULT 0,
                leader_ids VARCHAR(255) DEFAULT "",
                phone VARCHAR(20) DEFAULT "",
                sort INTEGER DEFAULT 0,
                remark TEXT,
                type INTEGER DEFAULT 1,
                status INTEGER DEFAULT 1,
                create_time INTEGER DEFAULT 0,
                update_time INTEGER DEFAULT 0
            )
        ');

        // 职位表
        $this->db->execute('
            CREATE TABLE IF NOT EXISTS oa_position (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title VARCHAR(50) NOT NULL,
                work_price INTEGER DEFAULT 0,
                remark TEXT,
                status INTEGER DEFAULT 1,
                create_time INTEGER DEFAULT 0,
                update_time INTEGER DEFAULT 0
            )
        ');

        // 管理员表
        $this->db->execute('
            CREATE TABLE IF NOT EXISTS oa_admin (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username VARCHAR(50) NOT NULL,
                name VARCHAR(50) NOT NULL,
                password VARCHAR(255) NOT NULL,
                did INTEGER DEFAULT 0,
                position_id INTEGER DEFAULT 0,
                group_id INTEGER DEFAULT 0,
                status INTEGER DEFAULT 1,
                create_time INTEGER DEFAULT 0,
                update_time INTEGER DEFAULT 0
            )
        ');

        // 管理员组表
        $this->db->execute('
            CREATE TABLE IF NOT EXISTS oa_admin_group (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title VARCHAR(50) NOT NULL,
                rules TEXT DEFAULT "",
                layouts TEXT DEFAULT "",
                desc TEXT DEFAULT "",
                status INTEGER DEFAULT 1,
                create_time INTEGER DEFAULT 0,
                update_time INTEGER DEFAULT 0
            )
        ');

        // 权限规则表
        $this->db->execute('
            CREATE TABLE IF NOT EXISTS oa_admin_rule (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                title VARCHAR(50) NOT NULL,
                name VARCHAR(50) NOT NULL,
                src VARCHAR(100) DEFAULT "",
                module VARCHAR(50) DEFAULT "",
                status INTEGER DEFAULT 1,
                create_time INTEGER DEFAULT 0,
                update_time INTEGER DEFAULT 0
            )
        ');
    }

    /**
     * 准备测试数据
     */
    protected function prepareTestData()
    {
        // 插入测试部门数据
        $departments = [
            ['id' => 1, 'title' => '总部', 'pid' => 0, 'status' => 1, 'sort' => 10, 'type' => 1, 'create_time' => time(), 'update_time' => time()],
            ['id' => 2, 'title' => '技术部', 'pid' => 1, 'status' => 1, 'sort' => 5, 'type' => 1, 'create_time' => time(), 'update_time' => time()],
            ['id' => 3, 'title' => '产品部', 'pid' => 1, 'status' => 1, 'sort' => 4, 'type' => 1, 'create_time' => time(), 'update_time' => time()],
        ];

        foreach ($departments as $dept) {
            $this->db->table('oa_department')->insert($dept);
        }

        // 插入测试职位数据
        $positions = [
            ['id' => 1, 'title' => '总经理', 'work_price' => 10000, 'status' => 1, 'create_time' => time(), 'update_time' => time()],
            ['id' => 2, 'title' => '技术总监', 'work_price' => 8000, 'status' => 1, 'create_time' => time(), 'update_time' => time()],
            ['id' => 3, 'title' => '产品经理', 'work_price' => 7000, 'status' => 1, 'create_time' => time(), 'update_time' => time()],
        ];

        foreach ($positions as $position) {
            $this->db->table('oa_position')->insert($position);
        }

        // 插入测试管理员组数据
        $groups = [
            ['id' => 1, 'title' => '超级管理员', 'rules' => '', 'status' => 1, 'create_time' => time(), 'update_time' => time()],
            ['id' => 2, 'title' => '普通管理员', 'rules' => '', 'status' => 1, 'create_time' => time(), 'update_time' => time()],
        ];

        foreach ($groups as $group) {
            $this->db->table('oa_admin_group')->insert($group);
        }

        // 插入测试用户数据
        $users = [
            ['id' => 1, 'username' => 'admin', 'name' => '管理员', 'password' => '123456', 'did' => 1, 'position_id' => 1, 'group_id' => 1, 'status' => 1, 'create_time' => time(), 'update_time' => time()],
            ['id' => 2, 'username' => 'user1', 'name' => '用户1', 'password' => '123456', 'did' => 2, 'position_id' => 2, 'group_id' => 2, 'status' => 1, 'create_time' => time(), 'update_time' => time()],
        ];

        foreach ($users as $user) {
            $this->db->table('oa_admin')->insert($user);
        }
    }

    /**
     * 清理测试数据
     */
    protected function cleanupTestData()
    {
        // 清空所有测试表
        $this->db->execute('DELETE FROM oa_admin');
        $this->db->execute('DELETE FROM oa_admin_group');
        $this->db->execute('DELETE FROM oa_position');
        $this->db->execute('DELETE FROM oa_department');
        $this->db->execute('DELETE FROM oa_admin_rule');

        // 重置自增ID
        $this->db->execute('DELETE FROM sqlite_sequence WHERE name IN (
            "oa_admin",
            "oa_admin_group",
            "oa_position",
            "oa_department",
            "oa_admin_rule"
        )');
    }

    /**
     * 清除缓存
     */
    protected function clearCache()
    {
        try {
            Cache::clear();
        } catch (\Exception $e) {
            // 忽略缓存清除错误
        }
    }

    /**
     * 关闭数据库连接
     */
    protected function closeDatabase()
    {
        try {
            Db::close();
        } catch (\Exception $e) {
            // 忽略关闭连接错误
        }
    }

    /**
     * 模拟数据数组
     * @param array $data 原始数据
     * @param array $overrides 覆盖数据
     * @return array
     */
    protected function mockData(array $data, array $overrides = []): array
    {
        return array_merge($data, $overrides);
    }

    /**
     * 生成随机字符串
     * @param int $length 长度
     * @return string
     */
    protected function generateRandomString(int $length = 10): string
    {
        return substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, $length);
    }

    /**
     * 生成随机数字
     * @param int $min 最小值
     * @param int $max 最大值
     * @return int
     */
    protected function generateRandomNumber(int $min = 1000, int $max = 9999): int
    {
        return rand($min, $max);
    }

    /**
     * 断言数组包含指定键
     * @param array $array
     * @param string|array $keys
     */
    protected function assertArrayHasKeys($array, $keys)
    {
        foreach ((array)$keys as $key) {
            $this->assertArrayHasKey($key, $array);
        }
    }

    /**
     * 断言数组不包含指定键
     * @param array $array
     * @param string|array $keys
     */
    protected function assertArrayNotHasKeys($array, $keys)
    {
        foreach ((array)$keys as $key) {
            $this->assertArrayNotHasKey($key, $array);
        }
    }
}
